import { useMessage } from '../../../hooks'
import { createInsCom } from '../../insCom'
import { addStyleToHead } from '../../style'
import {
  ref,
  computed,
  watch,
  onMounted,
  nextTick,
  unref,
  markRaw,
  shallowRef,
} from 'vue'
import { isFunction, isObject } from 'lodash-es'
import { getComAttrValue } from '../../utils/attrs'
import { getTsToJs, getInsBwUtils } from '../../utils/setupLogic'

import { getNodeComEmits } from '../../../config'

import { BwComTypeEnum } from '../../enums'

import {
  BwCustomComSoltTypeEnum,
  BwCustomComNodeAttrFromTypeEnum as BwAttrFromTypeEnum,
  BwCustomComEmitParamDataTypeEnum as BwParamDataTypeEnum,
  BwCustomComNodeEventCallbackParamFromTypeEnum as BwEventCallbackParamFromEnum,
} from '../../models'

import type { BwVueIns } from '../../utils/model'
import type {
  BwCustomComStructModel,
  BwCustomComEmitInfoModel,
  BwCustomComNodeAttrModel,
  BwCustomComDesignInfoModel,
  BwCustomComNodeEnableSlotInfoModel,
  BwCustomComConventionSlotInfoModel,
  BwCustomComNodeEventCallbackParamInfoModel,
} from '../../models'

export function useDesignEle(props: any) {
  const { createWarningModal } = useMessage()
  const exposes: Record<string, any> = {}

  const curNode = ref()
  const curEle = shallowRef<any>(null)
  const curCustomEmit = shallowRef<Array<any>>([])
  const curComSlotList = ref<Array<BwCustomComConventionSlotInfoModel>>([])

  const curComProps = computed(() => {
    const nodeInfo: BwCustomComStructModel = props.nodeInfo
    const curAttrs = getComAttrs(nodeInfo.attrs ?? {})

    const classList: Array<string> = []
    if (curAttrs.class) {
      classList.push(curAttrs.class)
    }
    return {
      ...curAttrs,
      nodeId: nodeInfo.nodeId,
      id: `com_${nodeInfo.nodeId}`,
      class: classList,
    }
  })

  /** 获取节点元素事件列表 */
  const getNodeEleEmitList = (nodeInfo?: BwCustomComStructModel) => {
    let list: Array<BwCustomComEmitInfoModel> = []
    const { comName, comType } = nodeInfo ?? {}

    switch (comType) {
      case BwComTypeEnum.ant:
      case BwComTypeEnum.html:
      case BwComTypeEnum.vxe:
      case BwComTypeEnum.vant: {
        list = getNodeComEmits(comName ?? '')
        break
      }
      case BwComTypeEnum.custom: {
        list = unref(curCustomEmit)
        break
      }
    }

    return list
  }

  const curComEvents = computed(() => {
    const evts: Record<string, Function> = {}
    const nodeInfo: BwCustomComStructModel = props.nodeInfo
    const curEmitList = getNodeEleEmitList(nodeInfo)

    if (Array.isArray(curEmitList) && curEmitList.length) {
      const vueIns: BwVueIns = props.vueIns
      const scopeData: Record<string, any> = props.scopeData

      const curAttrs = nodeInfo?.attrs ?? {}
      const curEvents = nodeInfo?.events ?? []

      curEmitList.forEach((item) => {
        const emitName = item.key
        const emitTexts = emitName.split(':')

        if (emitTexts[0] === 'update') {
          const modalField = emitTexts[1]
          const modalInfo: BwCustomComNodeAttrModel = curAttrs[modalField]
          if (
            modalInfo &&
            modalInfo.valueFromType === BwAttrFromTypeEnum.comVar
          ) {
            evts[emitName] = (value: any) => {
              const fields = (modalInfo.comVarName ?? '').split('.')

              if (fields.length === 1) {
                vueIns[fields[0]] = value
              } else {
                const len = fields.length
                let curValue: Record<string, any> = {}
                let curFieldValue: Record<string, any> = {}
                for (; fields.length > 0; ) {
                  const field = fields.pop()
                  if (field) {
                    fields.forEach((field, index) => {
                      if (index) {
                        isObject(curValue)
                          ? (curFieldValue = curValue[field])
                          : (curFieldValue = {})
                      } else {
                        curFieldValue = vueIns[field]
                      }
                    })

                    if (len - 1 === fields.length) {
                      curValue[field] = value
                    } else if (fields.length === 0) {
                      vueIns[field] = Object.assign(curFieldValue, curValue)
                    } else {
                      curValue = Object.assign(curFieldValue, curValue)
                    }
                  }
                }
              }
            }
          }
        } else {
          const eventInfo = curEvents.find((n: any) => n.eventCode === emitName)
          if (eventInfo) {
            let callbackFun = eventInfo.callbackFun ?? ''
            callbackFun = getTsToJs(callbackFun)
            callbackFun = callbackFun.replace('export default', 'return ')

            const fn = new Function('bwUtils', callbackFun)
            const callbackInfo = fn(
              getInsBwUtils(Object.assign({ params: {} }, props.extendUtils))
            )
            if (isFunction(callbackInfo.extnd)) {
              const callbackParams = eventInfo.callbackFunParams ?? []

              evts[emitName] = function (...data: any[]) {
                const params = eventInfo.eventParams
                const curEmitParams: Record<string, any> = {}
                if (Array.isArray(params) && params.length) {
                  params.forEach((item, index) => {
                    const name = item.paramKey
                    const value = data[index]

                    curEmitParams[name] = value
                  })
                }

                const curParams = getEventCallbackParams(
                  vueIns,
                  scopeData,
                  curEmitParams,
                  callbackParams
                )
                callbackInfo.extnd(curParams)
              }
            }
          }
        }
      })
    }
    return evts
  })

  /** 获取事件回调参数集合 */
  function getEventCallbackParams(
    vueIns: BwVueIns,
    scopeData: Record<string, any>,
    emitParams: Record<string, any>,
    data: Array<BwCustomComNodeEventCallbackParamInfoModel>
  ) {
    const params: Record<string, any> = {}
    if (Array.isArray(data) && data.length) {
      data.forEach((item) => {
        const name = item.paramCode
        if (name) {
          switch (item.paramFrom) {
            case BwEventCallbackParamFromEnum.const: {
              params[name] = getJSExpParamConstValue(item.paramDataType)
              break
            }
            case BwEventCallbackParamFromEnum.comVar: {
              params[name] = vueIns[item.comVarName ?? '']
              break
            }
            case BwEventCallbackParamFromEnum.comProp: {
              const curProps = vueIns.$props
              params[name] = curProps[item.comPropsName ?? '']
              break
            }
            case BwEventCallbackParamFromEnum.forVar: {
              const forVar = item.nodeForVarName ?? ''
              if (forVar) {
                const forData = scopeData.forData
                const [nodeId, field] = forVar.split('&')

                params[name] = (forData[nodeId] ?? {})[field]
              }

              break
            }
            case BwEventCallbackParamFromEnum.event: {
              params[name] = emitParams[item.eventParamName ?? '']
              break
            }
            case BwEventCallbackParamFromEnum.slotVar: {
              params[name] = scopeData
              break
            }
          }
        }
      })
    }
    return params
  }

  /** 获取 Js 表达式参数常量值*/
  function getJSExpParamConstValue(
    type: BwParamDataTypeEnum,
    constValue?: string
  ) {
    switch (type) {
      case BwParamDataTypeEnum.string: {
        return constValue
      }
      case BwParamDataTypeEnum.number: {
        return [undefined, null, '', 'null', 'undefined'].includes(constValue)
          ? undefined
          : Number(constValue)
      }
      case BwParamDataTypeEnum.boolean: {
        return ['true', true].includes(constValue ?? '')
      }
      case BwParamDataTypeEnum.array:
      case BwParamDataTypeEnum.object: {
        try {
          return constValue ? JSON.parse(constValue) : undefined
        } catch (error) {
          console.error(error)
          return undefined
        }
      }
      default: {
        return undefined
      }
    }
  }
  /** 获取组件属性 */
  const getComAttrs = (data: Record<string, BwCustomComNodeAttrModel>) => {
    const curAttrs: Record<string, any> = {}
    if (isObject(data)) {
      const vueIns: BwVueIns = props.vueIns
      const scopeData: Record<string, any> = props.scopeData
      Object.keys(data).forEach((key) => {
        const value = getComAttrValue(vueIns, data[key], scopeData)
        curAttrs[key] = value
      })
    }
    return curAttrs
  }

  /** 初始化组件插槽 */
  const initComSlotList = (
    data: Array<BwCustomComNodeEnableSlotInfoModel>,
    trendsSlotList: Array<BwCustomComConventionSlotInfoModel>
  ) => {
    curComSlotList.value = []
    if (Array.isArray(data) && data.length) {
      data.forEach((item: BwCustomComNodeEnableSlotInfoModel) => {
        if (item.visible) {
          switch (item.slotType) {
            case BwCustomComSoltTypeEnum.convention: {
              const info = item.conventionSlotInfo
              if (info) {
                curComSlotList.value.push(info)
              }
              break
            }
            case BwCustomComSoltTypeEnum.trends: {
              trendsSlotList.forEach((n) => {
                curComSlotList.value.push({
                  slotName: n?.slotName ?? '',
                  slotCode: n?.slotName ?? '',
                  slotParams: n?.slotParams,
                })
              })
              break
            }
          }
        }
      })
    }
  }

  /** 挂载节点元素样式 */
  const mountComCss = (nodeId: string, css: string | null) => {
    if (css) {
      addStyleToHead(`curtom-com-${nodeId}`, css)
    }
  }

  onMounted(() => {
    nextTick(() => {
      const ele = curNode.value
      if (ele) {
        Object.keys(ele).forEach((name) => {
          exposes[name] = ele[name]
        })
      }
    })
  })

  watch(
    () => props.nodeInfo,
    (newVal?: BwCustomComStructModel) => {
      if (newVal) {
        const nodeId = newVal.nodeId
        const comType = newVal.comType
        const comCode = newVal.comName ?? null
        const appCode = newVal.comAppCode ?? null
        const enableList = newVal.enableSlotList ?? []

        mountComCss(nodeId, newVal.css)
        if (comType === BwComTypeEnum.custom) {
          const extendUtils = props.extendUtils
          const getCustomComInfo = props.getCustomComInfo

          if (appCode && isFunction(getCustomComInfo)) {
            getCustomComInfo(appCode, comCode).then(
              (res: BwCustomComDesignInfoModel) => {
                if (res) {
                  curCustomEmit.value = res.emits ?? []
                  createInsCom(
                    res,
                    props.frontType,
                    getCustomComInfo,
                    extendUtils
                  ).then((insCom: any) => {
                    curEle.value = insCom ? markRaw(insCom) : null

                    nextTick(() => {
                      const nodeEle = unref(curNode)
                      if (isFunction(nodeEle.bwSysGetComSlotInfoList)) {
                        const list = nodeEle.bwSysGetComSlotInfoList()
                        initComSlotList(enableList, list)
                      }
                    })
                  })
                }
              }
            )
          } else {
            createWarningModal({
              content: `没有找到自定义组件【${comCode}】，请检查！`,
            })
          }
        } else {
          curEle.value = comCode
          initComSlotList(enableList, [])
        }
      }
    },
    { deep: true, immediate: true }
  )
  return {
    curEle,
    curNode,
    exposes,
    curComProps,
    curComEvents,
    curComSlotList,
  }
}
